// 导入依赖模块
var express = require('express');
var app = express();
require('express-ws')(app);

// 游戏配置
const PORT = 3000; // 后端部署端口
const PLAYER_ONE = 'p1';
const PLAYER_TWO = 'p2';
const CONSTANT = {
  round1: { // 每回合配置信息
    time: 40, // 回合限制时间
    num: 2, // 回合内可出牌数
  },
  round2: {
    time: 50,
    num: 2, 
  },
  round3: {
    time: 60,
    num: 2,
  },
  round4: {
    time: 60,
    num: 3,
  },
  initCardNum: 5, // 初始手牌数量
  addCardNum: 3, // 每回合补充手牌数量
  changeCardNum: 5, // 初始换牌次数
  addChangeCardNum: 1, // 每回合补充换牌次数
  fieldEffect: [ // 场地效果列表（前后端完全相同）
    {
      id: '1_none', // 场地效果ID
      name: '本区域无效果', // 场地效果名称
      extra: 'none', // meta数据
    },
    {
      id: '2_some1',
      name: '每有一张2，3，8，J加10分',
      extra: [ '2', '3', '8', 'J' ],
    },
    {
      id: '3_single1',
      name: '每有1张K加15分',
      extra: 'K',
    },
    {
      id: '4_color1',
      name: '每有1张红色牌加10分',
      extra: 'R',
    },
    {
      id: '5_color2',
      name: '若没有橙色牌加30分',
      extra: 'O',
    },
    {
      id: '6_color2',
      name: '每有1张蓝色牌加10分',
      extra: 'B',
    },
    {
      id: '7_type1',
      name: '本区域偶星额外加30分',
      extra: 'double',
    },
    {
      id: '8_principle1',
      name: '序列可以由4张牌组成',
      extra: 4,
    },
    {
      id: '9_principle2',
      name: '本区域无牌型加50分',
      extra: 'none',
    },
    {
      id: '10_code1',
      name: '点数和为奇数加25分',
      extra: 'single',
    },
  ],
};
let FieldEffect = CONSTANT.fieldEffect;

// 初始化玩家对象
const initPlayer = () => {
  return {
    uid: '',
    card: [], // 手牌
    status: 'wait',
    change: CONSTANT.changeCardNum, // 剩余换牌次数
    allow: 0, // 本回合允许出牌
  }
};
let player1 = initPlayer(); // 玩家1
let player1_cli = null; // 玩家1的websocket句柄
let player2 = initPlayer();
let player2_cli = null;

// 初始化游戏对象
const initGame = () => {
  return {
    status: 'wait',
    round: 1, // 当前回合
    time: 60, // 本回合时间
    cards: [ // 牌池（四种花色，每种花色2-A）
      // 红色
      {t: 'A', c: 'R', s: 12},{t: '2', c: 'R', s: 2},{t: '3', c: 'R', s: 3},{t: '4', c: 'R', s: 4},{t: '5', c: 'R', s: 5},
      {t: '6', c: 'R', s: 6},{t: '7', c: 'R', s: 7},{t: '8', c: 'R', s: 8},{t: '9', c: 'R', s: 9},{t: '10', c: 'R', s: 10},
      {t: 'J', c: 'R', s: 11},{t: 'Q', c: 'R', s: 11},{t: 'K', c: 'R', s: 11},
      // 绿色
      {t: 'A', c: 'G', s: 12},{t: '2', c: 'G', s: 2},{t: '3', c: 'G', s: 3},{t: '4', c: 'G', s: 4},{t: '5', c: 'G', s: 5},
      {t: '6', c: 'G', s: 6},{t: '7', c: 'G', s: 7},{t: '8', c: 'G', s: 8},{t: '9', c: 'G', s: 9},{t: '10', c: 'G', s: 10},
      {t: 'J', c: 'G', s: 11},{t: 'Q', c: 'G', s: 11},{t: 'K', c: 'G', s: 11},
      // 蓝色
      {t: 'A', c: 'B', s: 12},{t: '2', c: 'B', s: 2},{t: '3', c: 'B', s: 3},{t: '4', c: 'B', s: 4},{t: '5', c: 'B', s: 5},
      {t: '6', c: 'B', s: 6},{t: '7', c: 'B', s: 7},{t: '8', c: 'B', s: 8},{t: '9', c: 'B', s: 9},{t: '10', c: 'B', s: 10},
      {t: 'J', c: 'B', s: 11},{t: 'Q', c: 'B', s: 11},{t: 'K', c: 'B', s: 11},
      // 橙色
      {t: 'A', c: 'O', s: 12},{t: '2', c: 'O', s: 2},{t: '3', c: 'O', s: 3},{t: '4', c: 'O', s: 4},{t: '5', c: 'O', s: 5},
      {t: '6', c: 'O', s: 6},{t: '7', c: 'O', s: 7},{t: '8', c: 'O', s: 8},{t: '9', c: 'O', s: 9},{t: '10', c: 'O', s: 10},
      {t: 'J', c: 'O', s: 11},{t: 'Q', c: 'O', s: 11},{t: 'K', c: 'O', s: 11},
    ],
    // 每个区域的信息（pre:该区域预设牌 effect:该区域效果 p1:该区域玩家1放牌 p1s:该区域玩家分数）
    // p1s: [ { 第一回合分数对象 }, { 第二回合分数对象 }, ... ]
    field1: { pre: [], effect: { id: -1, name: '', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
    field2: { pre: [], effect: { id: -1, name: '', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
    field3: { pre: [], effect: { id: -1, name: '', extra: '' }, p1: [], p2: [], p1s: [], p2s: [] },
  };
};
let game = initGame(); // 对局信息

// 随机从牌池选出牌
function randSelAndDelete() {
  const len = game.cards.length;
  const index = Math.floor(Math.random() * len);
  const res = game.cards.splice(index, 1)[0];
  res.r = game.round; // 选出的牌属于当前回合
  return res;
}
// 随机选择场地效果
function randSelEffect() {
  const len = FieldEffect.length;
  const index = Math.floor(Math.random() * len);
  const res = FieldEffect.slice(index, index + 1)[0];
  return res;
}

// 第一回合
function round1Init() {
  console.log('回合1初始化开始');
  game.round = 1; // 当前游戏回合
  game.time = CONSTANT.round1.time; // 当前回合时间
  game.field1.effect = randSelEffect();
  game.field2.effect = { id: -1, name: '1回合后揭晓', extra: '' };
  game.field3.effect = { id: -1, name: '2回合后揭晓', extra: '' };
  game.field1.pre.push(randSelAndDelete()); // 区域1预设牌
  game.field1.pre.push(randSelAndDelete());
  for(let i = 0; i < CONSTANT.initCardNum; i++) { // 玩家1手牌
    player1.card.push(randSelAndDelete());
  }
  for(let i = 0; i < CONSTANT.initCardNum; i++) {
    player2.card.push(randSelAndDelete());
  }
  player1.allow = CONSTANT.round1.num; // 玩家1允许出牌数
  player2.allow = CONSTANT.round1.num;
  player1.status = 'gaming'; // 玩家1游戏状态
  player2.status = 'gaming';
  console.log(`还剩${game.cards.length}张牌`);
  console.log('回合1初始化结束');
}
// 第二回合
function round2Init() {
  console.log('回合2初始化开始');
  game.round = 2;
  game.time = CONSTANT.round2.time;
  game.field2.effect = randSelEffect();
  game.field3.effect = { id: -1, name: '1回合后揭晓', extra: '' };
  game.field2.pre.push(randSelAndDelete());
  game.field2.pre.push(randSelAndDelete());
  for(let i = 0; i < CONSTANT.addCardNum; i++) { // 每回合补充3张牌
    player1.card.push(randSelAndDelete());
  }
  for(let i = 0; i < CONSTANT.addCardNum; i++) {
    player2.card.push(randSelAndDelete());
  }
  player1.change += CONSTANT.addChangeCardNum; // 每回合补充n次换牌机会
  player2.change += CONSTANT.addChangeCardNum;
  player1.allow = CONSTANT.round2.num;
  player2.allow = CONSTANT.round2.num;
  player1.status = 'gaming';
  player2.status = 'gaming';
  player1.card = player1.card.map(cd => ({ ...cd, r: 2 })); // 当前手牌全部属于当前回合
  player2.card = player2.card.map(cd => ({ ...cd, r: 2 }));
  console.log(`还剩${game.cards.length}张牌`);
  console.log('回合2初始化结束');
}
// 第三回合
function round3Init() {
  console.log('回合3初始化开始');
  game.round = 3;
  game.time = CONSTANT.round3.time;
  game.field3.effect = randSelEffect();
  game.field3.pre.push(randSelAndDelete());
  game.field3.pre.push(randSelAndDelete());
  for(let i = 0; i < CONSTANT.addCardNum; i++) {
    player1.card.push(randSelAndDelete());
  }
  for(let i = 0; i < CONSTANT.addCardNum; i++) {
    player2.card.push(randSelAndDelete());
  }
  player1.change += CONSTANT.addChangeCardNum;
  player2.change += CONSTANT.addChangeCardNum;
  player1.allow = CONSTANT.round3.num;
  player2.allow = CONSTANT.round3.num;
  player1.status = 'gaming';
  player2.status = 'gaming';
  player1.card = player1.card.map(cd => ({ ...cd, r: 3 }));
  player2.card = player2.card.map(cd => ({ ...cd, r: 3 }));
  console.log(`还剩${game.cards.length}张牌`);
  console.log('回合3初始化结束');
}
// 第四回合
function round4Init() {
  console.log('回合4初始化开始');
  game.round = 4;
  game.time = CONSTANT.round4.time;
  for(let i = 0; i < CONSTANT.addCardNum; i++) {
    player1.card.push(randSelAndDelete());
  }
  for(let i = 0; i < CONSTANT.addCardNum; i++) {
    player2.card.push(randSelAndDelete());
  }
  player1.change += CONSTANT.addChangeCardNum;
  player2.change += CONSTANT.addChangeCardNum;
  player1.allow = CONSTANT.round4.num;
  player2.allow = CONSTANT.round4.num;
  player1.status = 'gaming';
  player2.status = 'gaming';
  player1.card = player1.card.map(cd => ({ ...cd, r: 4 }));
  player2.card = player2.card.map(cd => ({ ...cd, r: 4 }));
  console.log(`还剩${game.cards.length}张牌`);
  console.log('回合4初始化结束');
  // 结算
}

// 给指定玩家推送信息
function pushMessage(p = 1, msg = {}) {
  if(p === 1) { // 给P1玩家
    player1_cli.send(JSON.stringify(msg));
  } else { // P2
    player2_cli.send(JSON.stringify(msg));
  }
}
// 给玩家AB推送对局信息
function pushGameInfoAll() {
  pushMessage(1, { type: 'game', game, player1, player2 });
  pushMessage(2, { type: 'game', game, player1, player2 });
}

// 创建WebSocket连接
app.ws('/load', (client, req) => {
  client.on('message', msg => {
    let receive = undefined;
    try { receive = JSON.parse(msg); }
    catch { receive = {}; }
    // 根据不同类型消息做不同处理
    switch(receive.type) {
      case 'connect': { // 玩家连接系统
        if(player1.uid === '') {
          console.log('玩家1已连接');
          client._identity = PLAYER_ONE;
          player1.uid = receive.uid;
          player1_cli = client;
          pushMessage(1, { type: 'connect', status: PLAYER_ONE });
          break;
        }
        if(player2.uid === '') {
          console.log('玩家2已连接');
          client._identity = PLAYER_TWO;
          player2.uid = receive.uid;
          player2_cli = client;
          pushMessage(2, { type: 'connect', status: PLAYER_TWO });
          // player2完成连接后，推送玩家准备提示
          pushMessage(1, { type: 'connect', status: 'prepare' });
          pushMessage(2, { type: 'connect', status: 'prepare' });
          break;
        }
        // 用户已满
        client.send(JSON.stringify({ type: 'connect', status: 'fail' }));
        break;
      };
      case 'match': { // 玩家准备
        if(client._identity === PLAYER_ONE) {
          console.log('玩家1已准备');
          player1.status = 'prepared';
          pushMessage(1, { type: 'match', status: 'success' });
        }
        if(client._identity === PLAYER_TWO) {
          console.log('玩家2已准备');
          player2.status = 'prepared';
          pushMessage(2, { type: 'match', status: 'success' });
        }
        if(player1.status === 'prepared' && player2.status === 'prepared') {
          console.log('开始游戏');
          game = initGame();
          FieldEffect = CONSTANT.fieldEffect;
          game.status = 'gaming';
          round1Init(); // 初始化第一回合
          pushGameInfoAll(); // 给玩家AB推送初始对局信息
        }
        break;
      };
      case 'move': { // 将手牌移至场上某一区域
        if(receive.identity === PLAYER_ONE) {
          game[receive.to].p1.push(receive.card); // 对应玩家对应区域场上放入选定牌
          player1.card = player1.card.filter( // 对应玩家手牌删除选定牌
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
        } else { // p2
          game[receive.to].p2.push(receive.card);
          player2.card = player2.card.filter(
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
        }
        pushGameInfoAll();
        break;
      };
      case 'return': { // 将场上某一区域的牌移回手牌
        if(receive.identity === PLAYER_ONE) {
          player1.card.push(receive.card);
          game[receive.from].p1 = game[receive.from].p1.filter(
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
        } else { // p2
          player2.card.push(receive.card);
          game[receive.from].p2 = game[receive.from].p2.filter(
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
        }
        pushGameInfoAll();
        break;
      };
      case 'change': { // 玩家换牌
        if(receive.identity === PLAYER_ONE) {
          player1.card = player1.card.filter( // 手牌删除对应牌
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
          player1.card.push(randSelAndDelete()); // 增加新牌
          player1.change = player1.change - 1; // 换牌次数-1
          
        } else { // p2
          player2.card = player2.card.filter(
            cd => cd.t !== receive.card.t || cd.c !== receive.card.c
          );
          player2.card.push(randSelAndDelete());
          player2.change = player2.change - 1;
        }
        console.log(`还剩${game.cards.length}张牌`);
        pushGameInfoAll();
        break;
      };
      case 'over': { // 玩家当前回合结束
        if(receive.identity === PLAYER_ONE) {
          console.log(`玩家1第${receive.round}回合分数`, receive.score);
          player1.status = 'wait';
          // 赋值p1分数
          game.field1.p1s.push(receive.score[0]);
          game.field2.p1s.push(receive.score[1]);
          game.field3.p1s.push(receive.score[2]);
        }
        if(receive.identity === PLAYER_TWO) {
          console.log(`玩家2第${receive.round}回合分数`, receive.score);
          player2.status = 'wait';
          // 赋值p2分数
          game.field1.p2s.push(receive.score[0]);
          game.field2.p2s.push(receive.score[1]);
          game.field3.p2s.push(receive.score[2]);
        }
        if(player1.status === 'wait' && player2.status === 'wait') { // 转到下一回合
          if(game.round === 1) {
            round2Init();
          } else if(game.round === 2) {
            round3Init();
          } else if(game.round === 3) {
            round4Init();
          } else if(game.round === 4) {
            console.log('游戏结束并结算');
            game.status = 'finish';
            game.round = 5;
          }
        }
        pushGameInfoAll();
        break;
      };
      case 'resetclient': { // 重置连接
        console.log('后端已重置');
        player1.uid = '';
        player2.uid = '';
        player1_cli = null;
        player2_cli = null;
        break;
      };
    };
  });
  client.on('error', err => {
    console.log('#连接出错: ', err);
  });
  client.on('close', () => {
    if(client._identity === PLAYER_ONE) {
      console.log('玩家1已断连');
      player1 = initPlayer();
      player1_cli = null;
    }
    if(client._identity === PLAYER_TWO) {
      console.log('玩家2已断连');
      player2 = initPlayer();
      player2_cli = null;
    }
    game = initGame();
  });
});

app.get('/', (req, res) => {
  res.send(`后端启动成功[${new Date().getTime()}]`);
});

app.listen(PORT, () => {
  console.log('后端启动成功，部署端口为', PORT);
});
